package com.orange.core.security.repository;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.orange.core.security.detail.AdminDetail;
import com.orange.core.security.token.AdminAuthenticationToken;
import com.orange.core.security.token.AdminPersistenceToken;
import com.orange.core.util.Constants;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;

import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

@RequiredArgsConstructor
public class AdminSecurityContextRepository extends DelegatingContextRepository {

    private final ObjectMapper objectMapper;
    private final StringRedisTemplate redisTemplate;

    /**
     * 加载上下文
     **/
    @SneakyThrows
    @Override
    protected SecurityContext loadContext(SecurityContext securityContext, String token) {
        // 从redis获取当前用户保存的token字符串
        String tokenStr = redisTemplate.boundValueOps(Constants.ADMIN_TOKEN_REDIS_KEY + token).get();
        if (StrUtil.isNotBlank(tokenStr)) {
            // 将获取的json字符串，转化成权限对象，并存入上下文
            AdminPersistenceToken adminPersistenceToken = objectMapper.readValue(tokenStr, AdminPersistenceToken.class);
            securityContext.setAuthentication(adminPersistenceToken.toAuthenticationToken());
        }
        return securityContext;
    }

    @SneakyThrows
    @Override
    protected void saveContext(Authentication authentication, HttpServletResponse response) {
        if (authentication instanceof AdminAuthenticationToken adminAuthenticationToken && authentication.getPrincipal() instanceof AdminDetail adminDetail) {
            String token = adminDetail.getToken();
            BoundValueOperations<String, String> valueOps = redisTemplate.boundValueOps(Constants.ADMIN_TOKEN_REDIS_KEY + token);
            valueOps.set(objectMapper.writeValueAsString(AdminPersistenceToken.of((adminAuthenticationToken))));
            valueOps.expire(getMaxInactiveInterval(adminAuthenticationToken.getRememberMe()), TimeUnit.SECONDS);

            String username = authentication.getName();
            BoundSetOperations<String, String> setOps = redisTemplate.boundSetOps(Constants.ADMIN_TOKEN_REDIS_KEY + username);
            setOps.add(token);
            setOps.expire(getMaxInactiveInterval(adminAuthenticationToken.getRememberMe()), TimeUnit.SECONDS);

            response.setHeader(Constants.TOKEN_KEY, token);
        }
    }

    private long getMaxInactiveInterval(Boolean isRememberMe) {
        if (isRememberMe != null && isRememberMe) {
            return Constants.ADMIN_REMEMBER_ME_INACTIVE_INTERVAL;
        }
        return Constants.ADMIN_MAX_INACTIVE_INTERVAL;
    }
}
